package com.cloud.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cloud.common.core.cons.APICons;
import com.cloud.common.core.cons.AuthCons;
import com.cloud.common.core.model.entity.LoginInfo;
import com.cloud.common.core.model.response.FailedResponse;
import com.cloud.common.redis.utils.RedisUtils;
import com.cloud.gateway.utils.UrlMatchUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;


/**
 * 网关鉴权
 *
 * @author yan
 */
@Slf4j
@Order(-200)
@Component
public class AuthFilter implements GlobalFilter {

    /**
     * 白名单 todo 后期可配置yml进行动态读取
     */
    private final List<String> ignoreWhite = Arrays.asList(
            "/v2/api-docs", "/swagger-resources/**", "/swagger-ui.html", "/doc.html",
            "/**/favicon.ico", "/webjars/**", "/login", "/logout", "/open/**");

    @Autowired
    private RedisUtils redisUtils;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //request：127.0.0.1:8010/cloud-system/user/page = > requestUri => user/page
        String requestUri = exchange.getRequest().getURI().getPath();
        // 跳过不需要验证的路径
        if (UrlMatchUtils.matches(requestUri, ignoreWhite)) {
            return chain.filter(exchange);
        }
        String token = getToken(exchange.getRequest());
        if (StringUtils.isBlank(token)) {
            return sendUnauthorizedResponse(exchange, "令牌不能为空");
        }
        String userStr = redisUtils.get(AuthCons.USER_INFO_TOKEN + token);
        if (StringUtils.isBlank(userStr)) {
            return sendUnauthorizedResponse(exchange, "令牌已过期/令牌有误");
        }
        LoginInfo info = JSONObject.parseObject(userStr, LoginInfo.class);
        String username = info.getUserName();
        if (StringUtils.isBlank(username)) {
            return sendUnauthorizedResponse(exchange, "令牌验证失败");
        }

        // 设置用户信息到请求
        ServerHttpRequest mutableReq = exchange.getRequest().mutate()
                .header(APICons.API_OPER_NAME, username)
                .build();
        ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build();

        return chain.filter(mutableExchange);
    }

    private Mono<Void> sendUnauthorizedResponse(ServerWebExchange exchange, String msg) {
        ServerHttpResponse response = exchange.getResponse();
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);

        log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());

        return response.writeWith(Mono.fromSupplier(() -> {
            DataBufferFactory bufferFactory = response.bufferFactory();
            return bufferFactory.wrap(JSON.toJSONBytes(FailedResponse.builder()
                    .status(HttpStatus.UNAUTHORIZED.value())
                    .msg(msg)
                    .build()));
        }));
    }

    /**
     * 获取请求token
     */
    private String getToken(ServerHttpRequest request) {
        String token = request.getHeaders().getFirst(AuthCons.TOKEN_HEADER);
        if (StringUtils.isNotEmpty(token) && token.startsWith(AuthCons.TOKEN_PREFIX)) {
            token = token.replace(AuthCons.TOKEN_PREFIX, "");
        }
        return token;
    }
}